package cn.xw.config;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * @author AnHui OuYang
 * @version 1.0
 * created at 2023-04-18 15:16
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RabbitMQMyCallBack implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback {

    //注入rabbitTemplate对象
    private final RabbitTemplate rabbitTemplate;

    /***
     * 对象实例化完成（对象创建和属性注入）后调用此方法
     */
    @PostConstruct
    public void init() {
        //设置发布确认信息回调类RabbitTemplate.ConfirmCallback confirmCallback;
        rabbitTemplate.setConfirmCallback(this);
        //设置回退消息回调类ReturnsCallback.ReturnsCallback returnsCallback;
        rabbitTemplate.setReturnsCallback(this);
        //true：交换机无法将消息进行路由时，会将该消息返回给生产者；false：如果发现消息无法进行路由，则直接丢弃
        rabbitTemplate.setMandatory(true);  // 或使用配置   spring.rabbitmq.template.mandatory: true
    }

    /***
     * 发布确认（生产者-->交换机的确认）
     * @param correlationData 回调的相关数据
     * @param ack 消息是否成功发送给交换机，true成功，false失败
     * @param cause 对于ack为false时会有对应的失败原因，否则为空
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        //获取对应的ID消息，因为不确认是否有ID被传入，所以取值需要判空
        String id = correlationData == null ? "" : correlationData.getId();
        //校验是否成功发送
        if (ack) {
            log.info("消息已经成功交给了交换机，对应消息ID为：{}", id);
        } else {
            log.info("消息未能成功发送给交换机，对应消息ID为：{}，异常原因：{}", id, cause);
        }
    }

    /***
     * 当消息无法被路由时执行当前回调
     * @param returned 被退回的消息信息
     */
    @Override
    public void returnedMessage(ReturnedMessage returned) {
        // 发送的消息
        Message message = returned.getMessage();
        // 发送到哪个交换机
        String exchange = returned.getExchange();
        // 交换机到队列的路由key
        String routingKey = returned.getRoutingKey();
        // 退回原因
        String replyText = returned.getReplyText();
        // 退回原因状态码
        int replyCode = returned.getReplyCode();
        //消息打印
        log.info("信息被回退，从交换机：{} 路由：{} 发送到队列失败，发送信息为：{}，退回状态码：{} 和原因：{}",
                exchange, routingKey, message, replyCode, replyText);
        //我们可以在这后面对发送失败的消息进行处理
    }
}
